/**
    This library contains the definitions of the channel descriptor data structures
    and common functionalities that will be used for the implementation of the library
    (such as definition of delegation, queue,....).
    For internal use.
*/
#ifndef SHM_COMMON_H
#define SHM_COMMON_H

#include <sys/types.h>
#include <stdbool.h>

#include <pthread.h>
#include "synchr.h"

#define LINE_SIZE (16) // line size: 16 bytes to work with simd code
#define DQUEUE_SIZE 11 //initial size of the delegation queue
//#define LINE_SIZE (8) // line size: 8 bytes on ottavinareale
/* TODO (dematteis#1#): Gestione errori (communicator che nn parte ecc.ecc....) */

// returns the address of the i-th message of channel ch
#define MSG(ch,i) ( (char*) ch + sizeof(*ch) + ch->fixedsize*i )
// returns the address of the i-th validity byte of channel ch
#define VALB(ch,i) ( (char*) ch + sizeof(*ch) + ch->fixedsize*(ch->k+ch->j) + sizeof(bool)*i )

#define handle_error(en, msg) \
  { errno = en; perror(msg); return NULL; }


/**

   Alternative command descriptor.
   It contains a mutex for the mutual access, a condition variable for suspension/wakeup
   of the receiving process and the number of guards that compose it.
*/

/* TODO (dematteis#1#): Alternativo da modificare */
struct alternative_t
{
	key_t key;  //key for shared memory segment access
	int id;

	pthread_mutex_t mutex;
	pthread_cond_t cond;

	int n;
};

/**
    Channel descriptor structure
*/
struct channel_t
{
	union {
		struct {
			int type; // inheredited from base class (see shm.c)

			key_t key;
			int id;
			key_t altKey; // when set, receiver is receiving on this channel thru an Alternative

			lock_variable_t mutex;
			cond_variable_t cond_tosender; //condition variable used to signal an event towards the sender (by the receiver)
			cond_variable_t cond_toreceiver; //condition variable used to signal an event towards the receiver (by the sender)

			int size; // message size
			int fixedsize; // message size as a multiple of 8 bytes (due to cache line problems...)
			int messages; // sent messages counter (number of messages actually present in the channel)
			int k; // asynchrony grade (o in case of synchronous channel)
			int j; // how many static vtgs are allocated in memory
			int nextVtg; // which is the next vtg to use
			int currVtg; // last vtg used

		};
		char null[1024];
	};
};

/**
    Definition of a queue that will be used for the delegation from the worker thread to the
    communicator thread

*/
typedef struct{
    void **elements; //array containing the elements of the queue
    int readP; //read pointer
    int writeP; //write pointer
    int size; //size of the queue
    int numElem;// number of element actual present in queue

    lock_variable_t lv; //locking variable associated to the queue
    cond_variable_t cond; //condition variable for blocking procedures
}queue_t;



struct communicator_t{
    pthread_t id; //communicator id
    queue_t *dQueue; //queue that will contains the send delegation
};

/**
    Data structure for a send delegation from the worker to the communicator thread.
    It will contain a reference to the message and to the channel. It has also
    the necessary for the worker thread to be informed of the completion of that send
    (substantially a condition variable and the relative semaphore)
*/
 struct delegation_t{
    struct channel_t *ch; //pointer to the channel
    char *msg; //pointer to the message
    bool buffered :1; //says if this delegation regards a buffered send
    bool to_be_copied :1; //says if (in case of buffered send) the communicator has to make a copy of the message
    lock_variable_t lV; //lock variable (for waiting by the worker thread)
    cond_variable_t ticket; //condition variable (for waiting by the worker thread)
};



//functionalities that reagards the queue
void enqueue(queue_t *q, void *elem);

void *dequeue(queue_t *q);

queue_t *createQueue(int initialSize);

inline bool isFull(queue_t *q);

inline bool isEmpty(queue_t *q);

inline int getActualSize(queue_t *q);

//mutex and cond initialization
void mutex_init( pthread_mutex_t *mutex );
void cond_init( pthread_cond_t *cond );

//synchronization on the channel
inline void __LClockChannel( struct channel_t *ch );
inline void __LCunlockChannel( struct channel_t *ch );

//alternative command
void __LCwakeUpAlternative( key_t altKey );
inline void __LCsetAltKey( struct channel_t *ch, key_t altKey );
inline int __LClockAlt( struct alternative_t *alt );
inline int __LCunlockAlt( struct alternative_t *alt );
inline int __LCwaitOnAlt( struct alternative_t *alt );
inline int __LCwakeUpAlt( struct alternative_t *alt );

//print debug infos about the channel
void printCH_as(struct channel_t *ch);

#endif
